package ace.cmp.rest.spring.data.jdbc.querydsl.core.controller.admin;

import ace.cmp.core.model.R;
import ace.cmp.data.spring.data.jdbc.querydsl.rsql.core.manager.Manager;
import ace.cmp.data.spring.data.jdbc.querydsl.rsql.core.manager.impl.DefaultManagerConstructArgs;
import ace.cmp.data.spring.data.jdbc.querydsl.rsql.core.manager.security.SecurityManager;
import ace.cmp.data.spring.data.jdbc.querydsl.rsql.core.manager.security.impl.SecurityManagerImpl;
import ace.cmp.data.spring.data.jdbc.querydsl.rsql.core.manager.spring.ManagerSpringFactory;
import ace.cmp.security.api.SecurityService;
import ace.cmp.spring.data.jdbc.querydsl.core.QuerydslRelationHelper;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import java.util.List;
import java.util.Map;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.ResolvableType;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.web.PageableDefault;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;

/**
 * @author caspar
 * @date 2023/4/12 17:30
 */
@Slf4j
public abstract class AbstractAdminRestController<EntityT, IdT>
    implements InitializingBean {

  public static final String ADMIN_SAVE_URI = "/admin/save";
  public static final String ADMIN_SAVE_NOT_NULL_FIELD_URI = "/admin/saveNotNullField";
  public static final String ADMIN_ADD_URI = "/admin/add";
  public static final String ADMIN_ADD_NOT_NULL_FIELD_URI = "/admin/addNotNullField";
  public static final String ADMIN_MODIFY_URI = "/admin/modify";
  public static final String ADMIN_MODIFY_NOT_NULL_FIELD_URI = "/admin/modifyNotNullField";
  public static final String ADMIN_REMOVE_BY_ID_URI = "/admin/remove";
  public static final String ADMIN_REMOVE_LOGIC_BY_ID_URI = "/admin/removeLogic";
  public static final String ADMIN_GET_BY_ID_URI = "/admin/get";
  public static final String ADMIN_GET_LIST_BY_ID_IN_URI = "/admin/list";
  public static final String ADMIN_GET_MAP_BY_ID_IN_URI = "/admin/map";
  public static final String ADMIN_SEARCH_URI = "/admin/search";

  @Autowired
  private ManagerSpringFactory entityManagerSpringFactory;

  @Autowired
  @Getter(AccessLevel.PROTECTED)
  private QuerydslRelationHelper querydslRelationHelper;

  @Getter(value = AccessLevel.PROTECTED)
  @Setter(value = AccessLevel.PROTECTED)
  private Class<IdT> idClass;

  @Getter(value = AccessLevel.PROTECTED)
  @Setter(value = AccessLevel.PROTECTED)
  private Class<EntityT> entityClass;

  @Getter(value = AccessLevel.PROTECTED)
  @Setter(value = AccessLevel.PROTECTED)
  private Manager<EntityT, IdT> manager;

  @Autowired
  @Getter(AccessLevel.PROTECTED)
  @Setter(AccessLevel.PROTECTED)
  private SecurityService securityService;

  @Getter(AccessLevel.PROTECTED)
  @Setter(AccessLevel.PROTECTED)
  private SecurityManager securityManager;

  public AbstractAdminRestController() {
    ResolvableType resolvableType = ResolvableType.forType(this.getClass());
    this.entityClass = (Class<EntityT>) resolvableType.getSuperType().getGeneric(0).getRawClass();
    this.idClass = (Class<IdT>) resolvableType.getSuperType().getGeneric(1).getRawClass();
  }

  @Override
  public void afterPropertiesSet() throws Exception {
    this.initManager();
    securityManager = this.initSecurityManager();
  }


  @Operation(summary = "保存,新增或者修改")
  @PostMapping(ADMIN_SAVE_URI)
  public R<IdT> save(@Valid @NotNull @RequestBody EntityT entity) {
    this.securityManager.checkWriteAuthority();
    IdT id = this.getManager().saveEntityByConditionHasId(entity, false);
    return R.ok(id);
  }

  @Operation(summary = "保存,新增或者修改,不为null的字段")
  @PostMapping(ADMIN_SAVE_NOT_NULL_FIELD_URI)
  public R<IdT> saveNotNullField(@Valid @NotNull @RequestBody EntityT entity) {
    this.securityManager.checkWriteAuthority();
    IdT id = this.getManager().saveEntityByConditionHasId(entity, false);
    return R.ok(id);
  }

  @Operation(summary = "新增")
  @PostMapping(ADMIN_ADD_URI)
  public R<IdT> add(@Valid @NotNull @RequestBody EntityT entityInput) {
    this.securityManager.checkWriteAuthority();
    IdT id = this.getManager().addIgnore(entityInput, true);
    return R.ok(id);
  }

  @Operation(summary = "新增,不为null的字段")
  @PostMapping(ADMIN_ADD_NOT_NULL_FIELD_URI)
  public R<IdT> addNotNullField(@Valid @NotNull @RequestBody EntityT entityInput) {
    this.securityManager.checkWriteAuthority();
    IdT id = this.getManager().addIgnore(entityInput, false);
    return R.ok(id);
  }

  @Operation(summary = "修改")
  @PostMapping(ADMIN_MODIFY_URI)
  public R<IdT> modify(@Valid @NotNull @RequestBody EntityT entityInput) {
    this.securityManager.checkWriteAuthority();
    IdT id = this.getManager().modifyIgnore(entityInput, Boolean.TRUE);
    return R.ok(id);
  }

  @Operation(summary = "修改,不为null的字段")
  @PostMapping(ADMIN_MODIFY_NOT_NULL_FIELD_URI)
  public R<IdT> modifyNotNullField(@Valid @NotNull @RequestBody EntityT entityInput) {
    this.securityManager.checkWriteAuthority();
    IdT id = this.getManager().modifyIgnore(entityInput, Boolean.FALSE);
    return R.ok(id);
  }

  @Operation(summary = "删除")
  @PostMapping(ADMIN_REMOVE_BY_ID_URI)
  public R<Boolean> remove(@Valid @NotNull @NotEmpty @RequestBody List<IdT> ids) {
    this.securityManager.checkRemoveAuthority();
    Boolean isSuccess = this.getManager().remove(ids);
    return R.ok(isSuccess);
  }

  @Operation(summary = "逻辑删除")
  @PostMapping(ADMIN_REMOVE_LOGIC_BY_ID_URI)
  public R<Boolean> removeLogic(@Valid @NotNull @NotEmpty @RequestBody List<IdT> ids) {
    this.securityManager.checkRemoveAuthority();
    Boolean isSuccess = this.getManager().removeLogic(ids);
    return R.ok(isSuccess);
  }

  @Operation(summary = "获取明细")
  @GetMapping(ADMIN_GET_BY_ID_URI)
  public R<EntityT> getById(@Valid @NotNull @RequestParam("id") IdT id) {
    this.securityManager.checkReadAuthority();
    EntityT entityDto = this.getManager().getById(id);
    return R.ok(entityDto);
  }

  @Operation(summary = "根据id批量获取")
  @GetMapping(ADMIN_GET_LIST_BY_ID_IN_URI)
  public R<List<EntityT>> getListByIdIn(
      @Valid @NotNull @NotEmpty @RequestParam("ids") List<IdT> ids) {
    this.securityManager.checkReadAuthority();
    List<EntityT> entityDtoList = this.getManager().getListByIdIn(ids);
    return R.ok(entityDtoList);
  }

  @Operation(summary = "根据id批量获取,返回Map数据结构，key:实体id,value:实体")
  @GetMapping(ADMIN_GET_MAP_BY_ID_IN_URI)
  public R<Map<IdT, EntityT>> getMapByIdIn(
      @Valid @NotNull @NotEmpty @RequestParam("ids") List<IdT> ids) {
    this.securityManager.checkReadAuthority();
    Map<IdT, EntityT> entityDtoMap = (Map<IdT, EntityT>) this.getManager().getMapByIdIn(ids);
    return R.ok(entityDtoMap);
  }

  @Operation(summary = "rsql语法格式分页搜索")
  @GetMapping(ADMIN_SEARCH_URI)
  public R<Page<EntityT>> search(
      @RequestParam(value = "query", required = false) String query,
      @PageableDefault Pageable pageable) {
    this.securityManager.checkReadAuthority();
    Page<EntityT> entityDtoPage = this.getManager().search(query, pageable);
    return R.ok(entityDtoPage);
  }

  protected boolean hasAuthority(String authority) {
    return this.getSecurityService().hasAuthority(authority);
  }

  protected void initManager() {
    DefaultManagerConstructArgs<EntityT, IdT> args = new DefaultManagerConstructArgs<>();
    args.setIdClass(this.getIdClass());
    args.setEntityClass(this.getEntityClass());

    this.manager = entityManagerSpringFactory.getOrCreate(args);
  }

  protected SecurityManager initSecurityManager() {
    return new SecurityManagerImpl(this.getEntityClass(), this::hasAuthority);
  }
}
